package cn.mrcode.wxsdk.core.dialogue.common;

import cn.mrcode.wxsdk.core.dialogue.common.util.Util;
import cn.mrcode.wxsdk.core.dialogue.protocol.msg.InMsg;
import cn.mrcode.wxsdk.core.dialogue.protocol.msg.in.*;
import cn.mrcode.wxsdk.core.dialogue.protocol.msg.in.event.*;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;


/**
 * @Description: 请求消息解析工具类
 * @author zhuqiang
 * @version V1.0
 * @date 2015/8/19 15:13
 */
public class InMsgParaser {
    private InMsgParaser() {
    }

    /**
     * 从 xml 中解析出各类消息与事件,不支持加密模式
     *
     * @param xml xml 数据
     *
     * @return
     */
    public static InMsg parse(String xml) {
        try {
            return doParse(xml);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 消息类型 1：text 文本消息 2：image 图片消息 3：voice 语音消息 4：video 视频消息 5：location 地址位置消息
     * 6：link 链接消息 7：event 事件
     * 详细的消息类型在  @see MsgType 中能看到
     *
     * @param xml
     *
     * @return
     *
     *
     * @throws DocumentException
     */
    private static InMsg doParse(String xml) throws DocumentException {
        Document doc = DocumentHelper.parseText(xml);
        Element root = doc.getRootElement();
        String toUserName = root.elementText("ToUserName");
        String fromUserName = root.elementText("FromUserName");
        Integer createTime = Integer.parseInt(root.elementText("CreateTime"));
        String msgType = root.elementText("MsgType");
        if (MsgType.equals(MsgType.REQ_TEXT,msgType))
            return parseTextMsg(root, toUserName, fromUserName, createTime, msgType);
        if (MsgType.equals(MsgType.REQ_IMAGE,msgType))
            return parseImageMsg(root, toUserName, fromUserName, createTime, msgType);
        if (MsgType.equals(MsgType.REQ_VOICE,msgType))
            return parseVoiceMsgAndInSpeechRecognitionResults(root, toUserName, fromUserName, createTime, msgType);
        if (MsgType.equals(MsgType.REQ_VIDEO,msgType))
            return parseVideoMsg(root, toUserName, fromUserName, createTime, msgType);
        if (MsgType.equals(MsgType.REQ_LOCATION,msgType))
            return parseLocationMsg(root, toUserName, fromUserName, createTime, msgType);
        if (MsgType.equals(MsgType.REQ_LINK,msgType))
            return parseLinkMsg(root, toUserName, fromUserName, createTime, msgType);
        if (MsgType.equals(MsgType.REQ_EVENT,msgType))
            return parseEvent(root, toUserName, fromUserName, createTime, msgType);
        if(MsgType.equals(MsgType.REQ_SHORTVIDEO,msgType))
            return parseShortvideo(root, toUserName, fromUserName, createTime, msgType);

        throw new RuntimeException("无法识别的消息类型，请查阅微信公众平台开发文档");
    }



    /**
     * 解析请求消息，文本消息
     * @param root
     * @param toUserName 开发者微信号
     * @param fromUserName 发送方帐号（一个OpenID）
     * @param createTime 消息创建时间 （整型）
     * @param msgType 消息类型
     * @return
     */
    private static InMsg parseTextMsg(Element root, String toUserName,
                                       String fromUserName, Integer createTime, String msgType) {
        TextInMsg msg = new TextInMsg(toUserName, fromUserName, createTime, msgType);
        msg.setContent(root.elementText("Content"));
        msg.setMsgId(root.elementText("MsgId"));
        return msg;
    }

    private static InMsg parseImageMsg(Element root, String toUserName,
                                        String fromUserName, Integer createTime, String msgType) {
        ImageInMsg msg = new ImageInMsg(toUserName, fromUserName, createTime,
                msgType);
        msg.setPicUrl(root.elementText("PicUrl"));
        msg.setMediaId(root.elementText("MediaId"));
        msg.setMsgId(root.elementText("MsgId"));
        return msg;
    }

    private static InMsg parseVoiceMsgAndInSpeechRecognitionResults(
            Element root, String toUserName, String fromUserName,
            Integer createTime, String msgType) {
        String recognition = root.elementText("Recognition");
        // 判断是接收的是语音消息还是语音识别结果
        if ("".equals(recognition) || recognition == null) {
            VoiceInMsg msg = new VoiceInMsg(toUserName, fromUserName,
                    createTime, msgType);
            msg.setMediaId(root.elementText("MediaId"));
            msg.setFormat(root.elementText("Format"));
            msg.setMsgId(root.elementText("MsgId"));
            return msg;
        } else {
            SpeechRecognitionResults msg = new SpeechRecognitionResults(
                    toUserName, fromUserName, createTime, msgType);
            msg.setMediaId(root.elementText("MediaId"));
            msg.setFormat(root.elementText("Format"));
            msg.setMsgId(root.elementText("MsgId"));
            msg.setRecognition(recognition); // 与 InVoiceMsg 唯一的不同之处
            return msg;
        }
    }

    private static InMsg parseVideoMsg(Element root, String toUserName,
                                        String fromUserName, Integer createTime, String msgType) {
        VideoInMsg msg = new VideoInMsg(toUserName, fromUserName, createTime, msgType);
        msg.setMediaId(root.elementText("MediaId"));
        msg.setThumbMediaId(root.elementText("ThumbMediaId"));
        msg.setMsgId(root.elementText("MsgId"));
        return msg;
    }

    private static InMsg parseShortvideo(Element root, String toUserName, String fromUserName, Integer createTime, String msgType) {
        ShortVideoInMsg msg = new ShortVideoInMsg(toUserName,fromUserName,createTime,msgType);
        msg.setMediaId(root.elementText("MediaId"));
        msg.setThumbMediaId(root.elementText("ThumbMediaId"));
        msg.setMsgId(root.elementText("MsgId"));
        return msg;
    }



    private static InMsg parseLocationMsg(Element root, String toUserName,
                                           String fromUserName, Integer createTime, String msgType) {
        LocationInMsg msg = new LocationInMsg(toUserName, fromUserName,
                createTime, msgType);
        msg.setLocation_X(root.elementText("Location_X"));
        msg.setLocation_Y(root.elementText("Location_Y"));
        msg.setScale(root.elementText("Scale"));
        msg.setLabel(root.elementText("Label"));
        msg.setMsgId(root.elementText("MsgId"));
        return msg;
    }

    private static InMsg parseLinkMsg(Element root, String toUserName,
                                       String fromUserName, Integer createTime, String msgType) {
        LinkInMsg msg = new LinkInMsg(toUserName, fromUserName, createTime,
                msgType);
        msg.setTitle(root.elementText("Title"));
        msg.setDescription(root.elementText("Description"));
        msg.setUrl(root.elementText("Url"));
        msg.setMsgId(root.elementText("MsgId"));
        return msg;
    }

    /**
     * 解析四种事件
     * @param root dom rot元素
     * @param toUserName 开发者微信号
     * @param fromUserName 发送方帐号（一个OpenID）
     * @param createTime 消息创建时间 （整型）
     * @param msgType 消息类型，event
     * @return
     */
    private static InMsg parseEvent(Element root, String toUserName, String fromUserName, Integer createTime, String msgType) {
        String event = root.elementText("Event");
        String eventKey = root.elementText("EventKey");

        Util.log("event:" + event + "eventKey:" + eventKey);

        // 关注/取消关注事件（包括二维码扫描关注，二维码扫描关注事件与扫描带参数二维码事件是两回事）
        if (MsgType.equals(MsgType.EVT_SUBSCRIBE,event)
                || MsgType.equals(MsgType.EVT_UNSUBSCRIBE,event)
                && StringUtils.isBlank(eventKey)) {
            FollowInEvent e = new FollowInEvent(toUserName, fromUserName, createTime, msgType);
            e.setEvent(event);
            return e;
        }
        // 扫描带参数二维码事件之一 1: 用户未关注时，进行关注后的事件推送
        else if (MsgType.equals(MsgType.EVT_SUBSCRIBE,event)
                && StringUtils.isNotBlank(eventKey)
                && eventKey.startsWith("qrscene_")) {
            QrCodeInEvent e = new QrCodeInEvent(toUserName, fromUserName, createTime, msgType);
            e.setEvent(event);
            e.setEventKey(eventKey);
            e.setTicket(root.elementText("Ticket"));
            return e;
        }
        // 扫描带参数二维码事件之二 2: 用户已关注时的事件推送
        else if(MsgType.equals(MsgType.EVT_SCAN,event)) {
            QrCodeInEvent e = new QrCodeInEvent(toUserName, fromUserName, createTime, msgType);
            e.setEvent(event);
            e.setEventKey(eventKey);
            e.setTicket(root.elementText("Ticket"));
            return e;
        }
        // 上报地理位置事件
        else if (MsgType.equals(MsgType.EVT_LOCATION,event)) {
            LocationInEvent e = new LocationInEvent(toUserName, fromUserName, createTime, msgType);
            e.setEvent(event);
            e.setLatitude(root.elementText("Latitude"));
            e.setLongitude(root.elementText("Longitude"));
            e.setPrecision(root.elementText("Precision"));
            return e;
        }
        // 自定义菜单事件之一 1：点击菜单拉取消息时的事件推送
        else if (MsgType.equals(MsgType.EVT_MENU_CLICK,event)) {
            MenuInEvent e = new MenuInEvent(toUserName, fromUserName, createTime, msgType);
            e.setEvent(event);
            e.setEventKey(eventKey);
            return e;
        }
        // 自定义菜单事件之二 2：点击菜单跳转链接时的事件推送
        else if (MsgType.equals(MsgType.EVT_MENU_VIEW,event)) {
            MenuInEvent e = new MenuInEvent(toUserName, fromUserName, createTime, msgType);
            e.setEvent(event);
            e.setEventKey(eventKey);
            return e;
        }
        /** 以下自定义菜单事件 3-8 之间 ：仅支持微信iPhone5.4.1以上版本，和Android5.4以上版本的微信用户，旧版本微信用户点击后将没有回应，开发者也不能正常接收到事件推送。 **/
        // 自定义菜单事件之八 8：弹出地理位置选择器的事件推送
        else if (MsgType.equals(MsgType.EVT_MENU_LOCATION_SELECT,event)) {
            MenuLocationSelectInEvent e = new MenuLocationSelectInEvent(toUserName, fromUserName, createTime, msgType);
            e.setEvent(event);
            e.setEventKey(eventKey);
            MenuLocationSelectInEvent.SendLocationInfo sendLocationInfo = new MenuLocationSelectInEvent.SendLocationInfo();
            Element el = root.element("SendLocationInfo");
            sendLocationInfo.setLocation_X(el.elementText("Location_X"));
            sendLocationInfo.setLocation_Y(el.elementText("Location_Y"));
            sendLocationInfo.setScale(el.elementText("Scale"));
            sendLocationInfo.setLabel(el.elementText("Label"));
            sendLocationInfo.setPoiname(el.elementText("Poiname"));
            e.setSendLocationInfo(sendLocationInfo);
            return e;
        }

        // 模版消息发送完成之后的 上报
        else  if(MsgType.equals(MsgType.EVT_TEMPLATESENDJOBFINISH,event)){
            TemplateSendJobFinishInEvent e = new TemplateSendJobFinishInEvent(toUserName, fromUserName, createTime, msgType);
            e.setEvent(event);
            e.setMsgID(root.elementText("MsgID"));
            e.setStatus(root.elementText("Status"));
            return e;
        }

        throw new RuntimeException("无法识别的事件类型，请查阅微信公众平台开发文档");
    }

    @SuppressWarnings("unused")
    public static void main(String[] args) throws DocumentException {
        String xml = "<xml>\n" + "<ToUserName><![CDATA[James]]></ToUserName>\n"
                + "<FromUserName><![CDATA[JFinal]]></FromUserName>\n"
                + "<CreateTime>1348831860</CreateTime>\n"
                + "<MsgType><![CDATA[text]]></MsgType>\n"
                + "<Content><![CDATA[this is a test]]></Content>\n"
                + "<MsgId>1234567890123456</MsgId>\n" + "</xml>";

        // InTextMsg msg = (InTextMsg)parse(xml);
        // System.out.println(msg.getToUserName());
        // System.out.println(msg.getFromUserName());
        // System.out.println(msg.getContent());

        String xml_2 = "<xml>\n"
                + "<ToUserName><![CDATA[James]]></ToUserName>\n"
                + "<FromUserName><![CDATA[JFinal]]></FromUserName>\n"
                + "<CreateTime>1348831860</CreateTime>\n"
                + "<MsgType><![CDATA[text]]></MsgType>\n"
                + "<Content><![CDATA[this is a test]]></Content>\n"
                + "<MsgId>1234567890123456</MsgId>\n" + "</xml>";

        Document doc = DocumentHelper.parseText(xml_2);
        Element root = doc.getRootElement();
        String value = root.elementText("abc");
        System.out.println(value);
    }
}
